package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"time"

	context2 "kop/framework/context"
	"kop/libs/database"
	"kop/libs/redis"
	"kop/modules/island/configuration"
	"kop/modules/island/entity"
	"kop/modules/island/models"
	"kop/modules/item/consts"
	"kop/pb"
	"kop/pb/service"
	"kop/util/date"

	awardM "kop/modules/award/models"
	battleM "kop/modules/battle/models"
	guildC "kop/modules/guild/client"
	mailC "kop/modules/mail/client"
	roleC "kop/modules/role/client"
)

type IslandServiceHandler struct {
	context2.Context
}

func (h *IslandServiceHandler) Islands(ctx context.Context, req *wrappers.Int32Value, resp *pb.IslandsResp) error {

	var configs = configuration.GetIslandsByPage(req.Value)

	var islandIDs []int32
	for _, v := range configs {
		islandIDs = append(islandIDs, v.IslandID)
	}

	var islands = models.GetIslands(islandIDs, h.GetServerID(ctx))
	for k, island := range islands {

		if island.TimeOver() {

			if attach := h.finish(*island); attach != nil {

				var content, _ = json.Marshal(map[string]interface{}{"ID": 100})
				_ = mailC.Send1(island.RoleID, "(*IslandServiceHandler) Islands", string(content), attach)
			}

			islands[k] = nil
		}
	}

	resp.Islands = make([]*pb.Island, len(configs))
	for k, conf := range configs {

		resp.Islands[k] = &pb.Island{IslandID: conf.IslandID, Level: conf.Level, Income: conf.Income}

		for _, island := range islands {

			if island != nil && conf.IslandID == island.IslandID {

				resp.Islands[k].TotalIncome = island.Total()
				resp.Islands[k].Owner = &pb.IslandOwner{
					Name:      island.RoleName,
					GuildName: island.GuildName,
				}

				//if island.GuildID == guildID && island.RoleID != roleID {
				//	if conf.Level == 7 {
				//		addition = +10
				//	} else {
				//		addition += 1
				//	}
				//}
			}
		}
	}

	resp.Addition = h.addition(ctx, req.Value)
	return nil
}

func (h *IslandServiceHandler) Island(ctx context.Context, req *wrappers.Int32Value, resp *pb.Island) error {

	var island = models.GetIsland(req.Value, h.GetServerID(ctx))

	if island == nil {

		island = &entity.Island{IslandID: req.Value}

	} else if island.TimeOver() {

		if attach := h.finish(*island); attach != nil {

			var content, _ = json.Marshal(map[string]interface{}{"ID": 100})
			_ = mailC.Send1(island.RoleID, "(*IslandServiceHandler) Island", string(content), attach)
		}

		*island = entity.Island{IslandID: req.Value}
	}

	*resp = *buildPbIsland(island)
	return nil
}

func (h *IslandServiceHandler) Win(ctx context.Context, req *service.IslandWinRequest, resp *service.IslandBattleResp) error {

	var role, serverID = roleC.GetRole(h.GetRoleID(ctx)), h.GetServerID(ctx)

	if err := playNumSub1(role.RoleID); err != nil {
		return err
	}

	var island = models.GetIsland(req.IslandID, serverID)

	var set = func() {
		island.SetCaptains(req.Captains)
		island.RoleID = role.RoleID
		island.RoleName = role.Name
		island.Time = time.Now()
		island.GuildID = h.GetGuildID(ctx)

		if island.GuildID > 0 {
			var guild, err = guildC.GetGuild(serverID, island.GuildID)
			if err != nil {
				panic(err)
			}
			island.GuildName = guild.Name
		}

		resp.Island = buildPbIsland(island)
	}

	if island == nil {

		island = entity.NewIsland(req.IslandID, serverID)
		set()
		if err := models.Save(island); err != nil {
			return err
		}
	} else {

		var old = *island

		set()
		if err := models.Save(island); err != nil {
			return err
		}

		var itemNumTotal = old.Total()
		var num = itemNumTotal / 10

		_ = models.CreateReport(database.DB, entity.IslandReport{
			ServerID: serverID,
			RoleID:   old.RoleID,
			Attacker: role.RoleID,
			Photo:    role.Photo,
			Name:     role.Name,
			Name2:    old.RoleName,
			Level:    resp.Island.Level,
			Num:      num,
			Win:      true,
		})

		if num > 0 {
			resp.Award = &pb.Award{}
			resp.Award.AddRawItem(&pb.RawItem{ItemID: consts.Island, Number: num})
			if err := awardM.SendAward(role, resp.Award); err != nil {
				return err
			}

			// 排名
			addScore(h.GetGuildID(ctx), serverID, num)

			var content, _ = json.Marshal(map[string]interface{}{"ID": 101, "s1": role.Name})
			_ = mailC.Send1(old.RoleID, "(*IslandServiceHandler) Island", string(content), pb.CreateAward(consts.Island, itemNumTotal-num))

		}
	}

	resp.Addition = h.addition(ctx, configuration.GetByIslandID(island.IslandID).Page)
	resp.PlayNum, resp.PlayCD = getPlayNum(role.RoleID)
	return nil
}

func (h *IslandServiceHandler) Lost(ctx context.Context, req *wrappers.Int32Value, resp *service.IslandBattleResp) error {

	var roleID = h.GetRoleID(ctx)

	if err := playNumSub1(roleID); err != nil {
		return err
	}

	if island := models.GetIsland(req.Value, h.GetServerID(ctx)); island != nil {

		var attacker = roleC.GetRole(roleID)

		_ = models.CreateReport(database.DB, entity.IslandReport{
			RoleID:   island.RoleID,
			Attacker: attacker.RoleID,
			Photo:    attacker.Photo,
			Name:     attacker.Name,
			Win:      false,
			Name2:    island.RoleName,
			Level:    configuration.GetByIslandID(island.IslandID).Level,
		})
	}

	resp.PlayNum, resp.PlayCD = getPlayNum(roleID)
	return nil
}

func (h *IslandServiceHandler) Leave(ctx context.Context, req *wrappers.Int32Value, resp *pb.IslandLeaveResp) error {

	var roleID, serverID = h.GetRoleID(ctx), h.GetServerID(ctx)
	var island = models.GetIsland(req.Value, serverID)

	if haveOwner(island) && island.RoleID == roleID {

		if resp.Award = h.finish(*island); resp.Award != nil {

			if err := awardM.SendAward(&pb.Role{RoleID: roleID}, resp.Award); err != nil {
				return err
			}
		}

		var conf = configuration.GetByIslandID(req.Value)

		resp.Island = &pb.Island{}
		resp.Island.IslandID = conf.IslandID
		resp.Island.Level = conf.Level
		resp.Island.Power = conf.Power
		resp.Island.Income = conf.Income

		resp.Addition = h.addition(ctx, conf.Page)
	}

	return nil
}

func (h *IslandServiceHandler) GetIslands(_ context.Context, req *wrappers.Int32Value, resp *pb.Islands) error {

	var islands = models.GetIslandsByRoleID(req.Value)

	resp.Islands = make([]*pb.Island, 0, len(islands))
	for _, island := range islands {

		if island.TimeOver() {

			if attach := h.finish(island); attach != nil {

				var content, _ = json.Marshal(map[string]interface{}{"ID": 100})
				_ = mailC.Send1(island.RoleID, "(*IslandServiceHandler) GetIslands", string(content), attach)
			}

		} else {
			resp.Islands = append(resp.Islands, buildPbIsland(&island))
		}
	}

	return nil
}

func (h *IslandServiceHandler) MyIslands(ctx context.Context, _ *empty.Empty, resp *pb.MyIslandsResp) error {

	var roleID = h.GetRoleID(ctx)

	var islands = models.GetIslandsByRoleID(roleID)

	resp.Islands = make([]*pb.Island, 0, len(islands))
	for _, island := range islands {

		if island.TimeOver() {

			if attach := h.finish(island); attach != nil {

				var content, _ = json.Marshal(map[string]interface{}{"ID": 100})
				_ = mailC.Send1(island.RoleID, "(*IslandServiceHandler) MyIslands", string(content), attach)
			}

		} else {
			resp.Islands = append(resp.Islands, buildPbIsland(&island))
		}
	}

	resp.PlayNum, resp.PlayCD = getPlayNum(roleID)

	var report entity.IslandReport
	database.DB.Where(entity.IslandReport{RoleID: roleID}).Order("id DESC").First(&report)

	if report.ID > 0 {
		resp.ReportTime = int32(report.CreatedAt.Unix())
	}

	return nil
}

func (h *IslandServiceHandler) Reports(ctx context.Context, _ *empty.Empty, resp *pb.IslandReports) error {

	var res = models.GetReports(h.GetRoleID(ctx), 100)
	resp.Reports = make([]*pb.IslandReport, len(res))

	for k, v := range res {

		resp.Reports[k] = &pb.IslandReport{
			ID:        v.ID,
			RoleID:    v.Attacker,
			Photo:     v.Photo,
			Name:      v.Name,
			Win:       !v.Win,
			Num:       v.Num,
			Timestamp: int32(v.CreatedAt.Unix()),
		}
	}

	return nil
}

func (h *IslandServiceHandler) ServerReports(ctx context.Context, _ *empty.Empty, resp *pb.IslandReports) error {

	var data []entity.IslandReport
	database.DB.Where("server_id = ?", h.GetServerID(ctx)).Limit(20).Order("id DESC").Find(&data)

	resp.Reports = make([]*pb.IslandReport, len(data))
	for k, v := range data {

		resp.Reports[k] = &pb.IslandReport{
			ID: v.ID,
			//RoleID:    v.Attacker,
			Photo:     v.Photo,
			Name:      v.Name,
			Win:       v.Win,
			Num:       v.Num,
			Timestamp: int32(v.CreatedAt.Unix()),
			Name2:     v.Name2,
			Level:     v.Level,
		}
	}

	return nil
}

func (h *IslandServiceHandler) GuildRanking(ctx context.Context, _ *empty.Empty, resp *pb.GuildRankingResp) error {

	var guildID, serverID = h.GetGuildID(ctx), h.GetServerID(ctx)

	var awardsConfigs = configuration.RankingAwardList
	var limit = awardsConfigs[len(awardsConfigs)-1].RankMax

	var data = models.GetServerRanking(serverID, limit)

	resp.Ranking = make([]*pb.GuildRanking, len(data))
	for k, v := range data {
		resp.Ranking[k] = &pb.GuildRanking{
			Name:  v.Name,
			Flag:  v.Flag,
			Level: v.Level,
			Rank:  int32(k) + 1,
			Score: v.Score,
		}

		if guildID > 0 && v.GuildID == guildID {
			resp.MyRank = &pb.GuildRank{
				Rank:  int32(k) + 1,
				Score: v.Score,
			}
		}
	}

	if resp.MyRank == nil && guildID > 0 {
		if myRank := models.GetRanking(guildID); myRank != nil {

			var c int32 = 0
			database.DB.Model(myRank).Where("server_id = ?", serverID).Where("score > ?", myRank.Score).Count(&c)
			resp.MyRank = &pb.GuildRank{
				Rank:  c + 1,
				Score: myRank.Score,
			}
		}
	}

	return nil
}

func (h *IslandServiceHandler) RankingAward(_ context.Context, _ *empty.Empty, resp *pb.GuildAward) error {

	resp.LeaderAwards = configuration.GetLeaderAwards()
	resp.MemberAwards = configuration.GetMemberAwards()
	return nil
}

// 离岛然后 return物资
func (h *IslandServiceHandler) finish(island entity.Island) *pb.Award {

	var num = island.Total()
	var addition = h.addition(context2.WithUser(context.Background(), pb.User{RoleID: island.RoleID, ServerID: island.ServerID, GuildID: island.GuildID}), configuration.GetByIslandID(island.IslandID).Page)

	num += num * addition / 100

	// 排名
	addScore(island.GuildID, island.ServerID, num)

	var db = database.DB.Delete(&island)
	if db.Error != nil {
		panic(db.Error)
	}

	if db.RowsAffected > 0 && num > 0 {

		return pb.CreateAward(consts.Island, num)
	}

	return nil
}

func (h *IslandServiceHandler) addition(ctx context.Context, page int32) int32 {

	var configs = configuration.GetIslandsByPage(page)

	var islandIDs []int32
	for _, v := range configs {
		islandIDs = append(islandIDs, v.IslandID)
	}

	var addition int32 = 0

	for _, island := range models.GetIslands(islandIDs, h.GetServerID(ctx)) {

		if island.GuildID > 0 && island.GuildID == h.GetGuildID(ctx) && !island.TimeOver() {

			if configuration.GetByIslandID(island.IslandID).Level == 7 {

				addition += 10
			} else {
				addition += 1
			}
		}
	}

	return addition
}

// 定义存在redis里的json的结构体
type playNum struct {
	Num       int32
	Timestamp int32
}

const (
	MaxPlayNum int32 = 10
	RestoreSec int32 = 10 * 60 //10分钟恢复1点
)

func getPlayNum(roleID int32) (num int32, sec int32) {

	var data, err = redis.Get(fmt.Sprintf("IslandPlayNum%d", roleID))
	if err != nil {
		panic(err)

	} else if data != nil {

		var v playNum

		_ = json.Unmarshal(data, &v)

		var n, timestamp, now = v.Num, v.Timestamp, int32(time.Now().Unix())
		for i := MaxPlayNum - n; i > 0; i-- {

			timestamp += RestoreSec // 10分钟恢复1点
			if timestamp >= now {
				return n, timestamp - now
			}

			n += 1
		}
	}

	return MaxPlayNum, 0
}

func setPlayNum(roleID, n int32) error {

	var _, sec = getPlayNum(roleID)
	if sec == 0 {
		sec = RestoreSec
	}

	var v = playNum{
		Num:       n,
		Timestamp: int32(time.Now().Unix()),
	}

	if n < MaxPlayNum {
		v.Timestamp += sec - RestoreSec
	}

	var data, _ = json.Marshal(v)
	return redis.Set(fmt.Sprintf("IslandPlayNum%d", roleID), data, 600*10)
}

func playNumSub1(roleID int32) error {

	var n, _ = getPlayNum(roleID)
	if n <= 0 {
		return fmt.Errorf("num is 0")
	}

	return setPlayNum(roleID, n-1)
}

func haveOwner(island *entity.Island) bool {

	return island != nil && island.RoleID > 0
}

func buildPbIsland(island *entity.Island) *pb.Island {

	var conf = configuration.GetByIslandID(island.IslandID)
	var res = &pb.Island{
		IslandID: conf.IslandID,
		Level:    conf.Level,
		Income:   conf.Income,
	}

	if haveOwner(island) {

		res.TotalIncome = island.Total()
		res.Sec = int32(time.Now().Sub(island.Time).Seconds())
		res.Owner = &pb.IslandOwner{Name: island.RoleName, GuildName: island.GuildName}
		res.Captains = island.Captains.Captains
		for _, v := range res.Captains {
			res.Power += v.Power
		}
	} else {

		res.Power = conf.Power

		var getNpc = func(ID, level int32) *pb.Captain {
			npc, err := battleM.GetNpc(ID, conf.Level)
			if err != nil {
				panic(err)
			}
			return npc
		}

		npc1 := getNpc(conf.NPC1, conf.Level)
		npc2 := getNpc(conf.NPC2, conf.Level)
		npc3 := getNpc(conf.NPC3, conf.Level)
		npc4 := getNpc(conf.NPC4, conf.Level)
		npc5 := getNpc(conf.NPC5, conf.Level)

		res.Captains = []*pb.Captain{npc1, npc2, npc3, npc4, npc5}
	}

	return res
}

func initRanking(guildID, serverID, date int32) error {

	var data = models.GetRanking(guildID)
	if data == nil {

		var guild, err = guildC.GetGuild(serverID, guildID)
		if err != nil {
			return err
		}

		data = &entity.IslandGuildRanking{
			GuildID:  guildID,
			Name:     guild.Name,
			Flag:     guild.Flag,
			Level:    guild.Level,
			ServerID: guild.ServerID,
			Date:     date,
		}

		return database.DB.Create(data).Error
	} else if data.Date != date {

		data.Date = date
		data.Score = 0
		data.CreatedAt = time.Now()
		return database.DB.Save(data).Error
	}

	return nil
}

// 公会加分
func addScore(guildID, serverID, score int32) {

	if guildID > 0 && score > 0 {
		var t = time.Now()
		var week = t.Weekday()
		if week != 0 || t.Hour() < 10 {

			var thisDate = date.Date(time.Now().Add(time.Duration(-(week - 1)) * time.Hour * 24))

			_ = initRanking(guildID, serverID, thisDate)
			_ = models.AddScore(guildID, int64(score))
		}
	}
}
